import type {
  Ref,
} from 'react'
import {
  forwardRef,
  useEffect,
  useImperativeHandle,
  useRef,
} from 'react'
import { subscribeKey } from 'valtio/utils'
import { useSnapshot } from 'valtio'
import { useStore } from './store'
import { Message } from './Message'
import { initMonaco, monaco } from './initMonaco'
import { compress } from './utils/compression'
import { Tabs, TabsList, TabsTrigger } from './Tabs'

interface EditorProps {
  darkTheme?: boolean
  className?: string
}

function InnerEditor(
  { darkTheme, className }: EditorProps,
  ref: Ref<{
    resetCode: () => void
    getMonaco: () => monaco.editor.IStandaloneCodeEditor | undefined
  }>,
) {
  const containerRef = useRef<HTMLDivElement>(null)
  const isSwitchingRef = useRef(false)
  const store = useStore()
  const snapshot = useSnapshot(store)
  const monacoRef = useRef<monaco.editor.IStandaloneCodeEditor>()

  useImperativeHandle(ref, () => ({
    resetCode: () => {
      monacoRef.current?.setValue(store.code)
    },
    getCode: () => monacoRef.current?.getValue(),
    getCompressed: () => {
      return compress(JSON.stringify(store.files))
    },
    getMonaco: () => monacoRef.current,
  }))

  useEffect(() => {
    initMonaco()
    if (!containerRef.current)
      throw new Error('container is not defined')

    const monacoInst = monaco.editor.create(containerRef.current, {
      value: store.code,
      language: 'javascript',
      theme: darkTheme ? 'vs-dark' : 'vs',
      minimap: {
        enabled: false,
      },
      scrollBeyondLastLine: false,
    })

    monacoInst.onDidChangeModelContent(() => {
      if (isSwitchingRef.current) {
        isSwitchingRef.current = false
        return
      }
      if (store.code !== monacoInst.getValue())
        store.updateCode(monacoInst.getValue())
    })

    new ResizeObserver(() => {
      monacoInst.layout()
    }).observe(containerRef.current)
    monacoRef.current = monacoInst

    subscribeKey(store, 'currentFile', () => {
      isSwitchingRef.current = true
      monacoRef.current?.setValue(store.code)

      const lang = store.currentFile.endsWith('.css') ? 'css' : 'javascript'
      const model = monacoRef.current?.getModel()
      if (model)
        monaco.editor.setModelLanguage(model, lang)
    })

    subscribeKey(store, 'code', () => {
      if (store.code !== monacoInst.getValue())
        monacoInst.setValue(store.code)
    })

    return () => monacoInst.dispose()
  }, [])

  return (
    <div className={`relative w-full h-full bg-white ${className}`}>
      {snapshot.enableTabs && (
        <Tabs value={snapshot.currentFile}>
          <TabsList className="h-9">
            {
            Object.keys(snapshot.files).map((file, index) => (
              <TabsTrigger
                key={index}
                value={file}
                onClick={() => {
                  store.switchFile(file)
                }}
                onDoubleClick={() => {
                  // eslint-disable-next-line no-alert
                  const newName = prompt('New File Name\n e.g. Comp1.jsx or style.css')
                  if (newName)
                    store.changeFileName({ oldName: file, newName })
                }}
              >
                {file}
                {index
                  ? (
                    <span
                      className="ml-2 cursor-pointer"
                      onClick={(e) => {
                        e.stopPropagation()
                        // eslint-disable-next-line no-alert
                        if (confirm(`Are you sure to delete ${file}?`))
                          store.deleteFile(file)
                      }}
                    >
                      ✕
                    </span>
                    )
                  : ''}
              </TabsTrigger>
            ))
          }
            <button
              className="border-0 bg-white text-xl cursor-pointer select-none"
              onClick={() => {
                store.addFile()
              }}
            >
              +
            </button>
          </TabsList>
        </Tabs>
      )}
      <div className="w-full h-full " ref={containerRef} />
      <Message
        message={snapshot.compileError}
        onDismiss={() => store.clearCompileError()}
      />
    </div>
  )
}

const Editor = forwardRef(InnerEditor)
export { Editor }
